﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using Kokomo.Lexical.Expressions;
using Kokomo.Lexical.Coding;

namespace Kokomo.PeInspector.Reflection.Disassembly.Flow
{
	class ExceptionHandlerFlowAnalyzer : FlowAnalyzer
	{
		public Type ExceptionType { get; private set; }
		public List<Statement> CatchStatements { get; private set; }
		public LocalVariable ExceptionVariable { get; private set; }

		public ExceptionHandlerFlowAnalyzer(
			FlowAnalyzer parentAnalyzer,
			int blockStartOffset,
			int blockEndOffset,
			Type exceptionType
			)
			: base(parentAnalyzer, blockStartOffset, blockEndOffset, blockStartOffset, blockEndOffset)
		{
			this.ExceptionType = exceptionType;
			this.CatchStatements = new List<Statement>();
			this.ActiveStatementBlock = this.CatchStatements;
		}

		protected override void AnalyzeInstruction()
		{
			MsilDecompilationContext context = this.Context;
			Instruction instruction = context.CurrentInstruction;

			if (
				(
					(instruction.OpCode == OpCodes.Stloc)
					|| (instruction.OpCode == OpCodes.Stloc_S)
				)
				&& (context.ExpressionCount == 1)
				&& (context.PeekExpression() is ExceptionExpression)
				)
			{
				int exceptionVarNumber;
				if (instruction.OpCode == OpCodes.Stloc)
					exceptionVarNumber = (int)instruction.Operand;
				else /* if (instruction.OpCode == OpCodes.Stloc_S) */
					exceptionVarNumber = (byte)instruction.Operand;

				this.ExceptionVariable = context.LocalVariables[exceptionVarNumber];
				this.ExceptionVariable.Name = "ex";
				context.PopExpression();
				context.Advance();
			}
			else
			{
				base.AnalyzeInstruction();
			}
		}

		protected override void OnEnterBlock()
		{
			base.OnEnterBlock();
			if (this.ExceptionType != null)
			{
				this.Context.PushStackEntry(new ExceptionExpression(), this.ExceptionType);
			}
		}
	}
}
